package imgo

import (
	"net"
	"sync"
)

//Center 控制中心
type Center struct {
	//routineMap 工作携程与链接的Map
	routineMap map[*net.Conn]*WorkRoutine
	//chanExit 当携程关闭后的删除信号信道
	chanExit chan *net.Conn
	//mapUsed map锁
	mapUsed *sync.RWMutex
}

//NewCenter center初始化方法
//capacity 连接存储空间大小 可理解为预制用户数量
func NewCenter(capacity int) *Center {
	//初始化信道与集合
	channelDel := make(chan *net.Conn, capacity)
	routineMap := make(map[*net.Conn]*WorkRoutine, capacity)
	var mapUsed sync.RWMutex
	//创建对象
	m := Center{
		routineMap: routineMap,
		chanExit:   channelDel,
		mapUsed:    &mapUsed,
	}
	//启动各自携程
	go m.exit()
	go m.catchIn()
	//返回对象
	return &m
}

// distribute 分发新建携程 此方法与connector同步
func (m *Center) distribute(iWorker IWorkRoutine, worker *WorkRoutine) {
	//加锁map记录
	m.mapUsed.Lock()
	m.routineMap[worker.Conn] = worker
	m.mapUsed.Unlock()
	//传入信道执行routine
	go IWorkRoutine.ReadListener(iWorker, worker)
	go IWorkRoutine.WriteLooper(iWorker, worker)
}

// exit 通过Exit信道删除
//由 NewCenter 核心开启携程
func (m *Center) exit() {
	chanFinish := make(chan bool)
	for {
		if conn, ok := <-m.chanExit; ok {
			//已经被删除的情况不要重复删除
			worker := m.routineMap[conn]
			if worker == nil {
				continue
			}
			//执行伪异步安全关闭
			go m.safetyClose(conn, worker, chanFinish)
			ok := <-chanFinish
			if !ok {
				Logger.Err().Println(" -- close some connection or channel or delete worker failed !")
			}
		}
	}
}

// safetyClose 安全关闭掉 conn worker.chan
//以及执行完成后向同步信道发送信息以继续
func (m *Center) safetyClose(conn *net.Conn, worker *WorkRoutine, finish chan bool) {
	defer func() {
		err := recover()
		if conn != nil {
			globalClose <- conn
		}
		if err != nil {
			finish <- false
			Logger.Err().Println(" -- safety close throw err : ", err)
		} else {
			finish <- true
		}
	}()
	//关闭信道
	worker.ConnLocker.Lock()
	if worker.ChanIn != nil {
		close(worker.ChanIn)
		worker.ChanIn = nil
	}
	if worker.ChanOut != nil {
		close(worker.ChanOut)
		worker.ChanOut = nil
	}
	worker.ConnLocker.Unlock()
	//加锁删除map对应键
	m.mapUsed.Lock()
	delete(m.routineMap, conn)
	m.mapUsed.Unlock()
}

// catchIn 通过globalIn信道获取输入 并放入全局消息处理队列
//由 NewCenter 核心开启携程
func (m *Center) catchIn() {
	for {
		m.mapUsed.RLock()
		for conn, r := range m.routineMap {
			if len(r.ChanIn) != 0 {
				if originMsg, ok := <-r.ChanIn; ok {
					globalIn <- &Message{From: conn, OriginMsg: originMsg}
				}
			}
		}
		m.mapUsed.RUnlock()
	}
}
